﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyUtils.Core
{
    public interface IJob
    {
        /// <summary>
        /// 工作名
        /// </summary>
        string Name { get; }
        /// <summary>
        /// 获取工作倒计时
        /// </summary>
        /// <returns></returns>
        TimeSpan GetRunTime();
        /// <summary>
        /// 工作内容
        /// </summary>
        void Run();
        /// <summary>
        /// 工作中异常处理
        /// </summary>
        /// <param name="ex"></param>
        RetryResult ReTry(Exception ex);
    }

    public enum RetryResult
    {
        /// <summary>
        /// 设置为Break等待下一次执行
        /// </summary>
        Break,
        /// <summary>
        /// 设置为 ContinueOnec 会重试一次
        /// </summary>
        ContinueOnce,
        /// <summary>
        /// 设置为 Continue 会重新执行直到不为Continue为止
        /// </summary>
        Continue,
    }

    internal class JobContext
    {
        public IJob Job { get; set; }
        public TimeSpan JobTime { get; set; }
        public Func<RetryResult> JobAction { get; set; }
        public Task JobTask { get; set; }
        /// <summary>
        /// 实例化工作中间件
        /// </summary>
        /// <param name="job"></param>
        /// <param name="nowRun">立刻执行？</param>
        public JobContext(IJob job,bool nowRun = true)
        {
            Job = job;
            JobTime = nowRun ? TimeSpan.Zero : job.GetRunTime();
            JobAction = new Func<RetryResult>( () =>
            {
                try
                {
                    Job.Run();
                }
                catch (Exception ex)
                {
                    return Job.ReTry(ex);
                }
                return RetryResult.Break;
            });

            JobTask = null;

        }

       
    }

    /// <summary>
    /// 打工人
    /// </summary>
    public class JobMan
    {
        private List<JobContext> _jobCtxList;
        private TimeSpan _sleepSec = new TimeSpan(0, 0, 0, 1);
        private Task _main;
        private TaskScheduler _taskSc;

        public JobMan()
        {
            _jobCtxList = new();
        }

        /// <summary>
        /// 添加工作
        /// </summary>
        /// <param name="job"></param>
        /// <param name="nowRun">立刻执行？</param>
        /// <exception cref="Exception"></exception>
        public void AddJob(IJob job,bool nowRun = true)
        {
            _jobCtxList.Add(
                new JobContext(job,nowRun)
            ) ;
        }

        /// <summary>
        /// 删除工作
        /// </summary>
        /// <param name="job"></param>
        public void RemoveJob(IJob job) 
        {
            int idx = _jobCtxList.Select(x=>x.Job).ToList().IndexOf(job);
            var ctx = _jobCtxList[idx];
            if (ctx.JobTask == null)
                _jobCtxList.RemoveAt(idx);
            else
                throw new Exception($"{ctx.Job.Name} 正在运行,禁止删除");
        }

        /// <summary>
        /// 同步运行
        /// </summary>
        /// <param name="consoleDebug">JobMan打印输出Debug开关</param>
        /// <exception cref="Exception"></exception>
        public void Run(bool consoleDebug = false)
        {

            int jobCount = 0;
            if (_jobCtxList.Count == 0)
                throw new Exception("无工作内容");
            List<JobContext> jobCtxListBak = null;
            while (true)
            {
                if (jobCount != _jobCtxList.Count)
                {
                    if (consoleDebug)
                        Console.WriteLine($"工作线程数量发生变更");
                    _taskSc = new LimitedConcurrencyLevelTaskScheduler(_jobCtxList.Count); // 动态调整工作线程上限
                    jobCtxListBak = new List<JobContext>(_jobCtxList); // 动态调整工作线程上下文列表
                    jobCount = _jobCtxList.Count;
                }

                for (int i = 0; i < jobCount; i++) 
                {
                    var jobCtx = jobCtxListBak[i];
                    if (jobCtx == null)
                        continue;

                    jobCtx.JobTime -= _sleepSec;
                    if (consoleDebug)
                        Console.WriteLine($"{jobCtx.Job.Name} 执行倒计时:{jobCtx.JobTime.TotalSeconds}");
                    if (jobCtx.JobTime <= TimeSpan.Zero)
                    {
                        if (jobCtx.JobTask == null) //时间到且没有正在执行任务=> 加载task
                        {
                            if (consoleDebug)
                                Console.WriteLine($"{jobCtx.Job.Name} 准备任务线程");
                            jobCtx.JobTask = Task.Factory.StartNew(() =>
                            {
                                var reTryTime = 1;
                                var jobRet = RetryResult.Break;
                                do
                                {
                                    try
                                    {
                                        jobRet = jobCtx.JobAction();
                                    }
                                    catch (Exception ex) // 在retry中炸了 也要catch住 确保框架不会炸
                                    {
                                        if (consoleDebug && jobCtx != null && jobCtx.Job != null && string.IsNullOrEmpty(jobCtx.Job.Name) == false)
                                            Console.WriteLine($"{jobCtx.Job.Name} Retry失败报错:{ex.Message}");
                                    }
                                } while (jobRet == RetryResult.Continue || (jobRet == RetryResult.ContinueOnce && reTryTime -- > 0));
                            }, CancellationToken.None, TaskCreationOptions.None, _taskSc);
                        }
                        switch (jobCtx.JobTask.Status) // 判断任务状态
                        {
                            // 时间到线程准备好了直接执行
                            case TaskStatus.Created:
                            case TaskStatus.WaitingForActivation:
                            case TaskStatus.WaitingToRun:
                                if (consoleDebug)
                                    Console.WriteLine($"{jobCtx.Job.Name} 状态:{jobCtx.JobTask.Status} 准备启动阶段");
                                break;
                            case TaskStatus.Running:
                            case TaskStatus.WaitingForChildrenToComplete:
                                if (consoleDebug)
                                    Console.WriteLine($"{jobCtx.Job.Name} 状态: {jobCtx.JobTask.Status} 运行中(提示:可能存在异常在ReTry循环中)");
                                break;
                            // 时间到了 task为完成态后重新计时
                            case TaskStatus.RanToCompletion:
                            case TaskStatus.Canceled:
                            case TaskStatus.Faulted:
                                if (consoleDebug)
                                    Console.WriteLine($"{jobCtx.Job.Name} 状态: {jobCtx.JobTask.Status} 工作结束(重新计数)");
                                jobCtx.JobTask = null;
                                jobCtx.JobTime = jobCtx.Job.GetRunTime();
                                break;
                            default:
                                break;
                        }
                    }
                }
                Task.Delay(_sleepSec).Wait();
            }
        }
    
        public async Task RunAsync(bool consoleDebug = false)
        {
            _main = Task.Run(() =>
            {
                Run(consoleDebug);
            });
        }
    }
}
